package information;


import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.swing.JOptionPane;

/**
 * Class for managing the sets of Users and Jobs.
 * @author Christian
 * @author Barry (validateJob(), validateVolunteer())
 * @author Natalie (validateJob(), getJobOnDay())
 */
public class Data {

	private User myCurrentUser;
	
	private Set<Job> myArchivedJobs;
	
	private Set<Job> myCurrentJobs;
	
	private Set<User> myUsers;
	
	public Data() {
		myArchivedJobs = new HashSet<Job>();
		myCurrentJobs = new HashSet<Job>();
		myUsers = new HashSet<User>();
		myCurrentUser = null;
	}
	
	/**
	 * Initializes model.
	 */
	//@requires backing store is in spot required by FileIO class and is formatted correctly
	//@ensures sets of Jobs are created from backing store
	public void start() {
		FileIO file = new FileIO();
		myUsers = file.createUsers();
		file.createJobs(myCurrentJobs, myArchivedJobs);
	}
	
	/**
	 * Checks if email is in set of Users.
	 * If so, sets field to User.
	 * @param theName
	 * @param theEmail
	 */
	//@ensures User is set based on email and 
	public void login(final String theName, final String theEmail) {
		for (User user : myUsers) {
			if (user.getName().equals(theName) && user.getEmail().equals(theEmail)) {
				myCurrentUser = user;
				break;
			}
		}
	}
	
	public Set<Job> getArchivedJobs() {
		return myArchivedJobs;
	}
	
	public Set<Job> getCurrentJobs() {
		return myCurrentJobs;
	}
	
	public Set<User> getVolunteers() {
		Set<User> volunteers = new HashSet<User>();
		for (User user : myUsers) {
			if (user.getUsertype() == Usertype.VOLUNTEER) {
				volunteers.add(user);
			}
		}
		return volunteers;
	}
	
	/**
	 * Creates a job with data passed in.
	 * @param theManager
	 * @param theLevelDetails
	 * @param theLevelNumbers
	 * @param theStartDate
	 * @param theEndDate
	 * @param theDescription
	 * @param theWeekday
	 * @param theHoliday
	 * @param thePark
	 * @return The Job created from the passed in parameters.
	 */
	public boolean createNewJob(final User theManager, final List<String> theLevelDetails, final List<Integer> theLevelNumbers, 
			final Date theStartDate, final Date theEndDate, final String theDescription, final boolean theWeekday, final boolean theHoliday, final Park thePark) {
		boolean added = false;
		Job newJob = new Job(theManager, theLevelDetails, theLevelNumbers, theStartDate,  theEndDate, theDescription, theWeekday, theHoliday, thePark);
		if(validateJob(newJob)) {
			myCurrentJobs.add(newJob);
			added = true;
		}
		return added;
	}
	
	/**
	 * Validates that a job can be added to the system. Checks that the total number of jobs is less than 30 (BR#1), 
	 * that the number of pending jobs that week is < 5 (BR#2), that the job does not last more than 2 days (BR#4), 
	 * and that the job is not in the past or more than 3 months in the future (BR#5). If any of these errors are 
	 * made and the job is not valid a message will be displayed to the user notifying them of what is wrong. 
	 * @author Natalie
	 * @author Barry
	 * @param theJob
	 * @return True if the job is valid, false if it violates a business rule. 
	 */
	private boolean validateJob(final Job theJob) {
		boolean valid = true;
		int jobcount =0; 
		if(myCurrentJobs.size() == 30) {
			valid = false;
			JOptionPane.showMessageDialog(null, "No more jobs can be scheduled at this time. Please come back later!");
		}
		//false is job lasts more than two days
		if (theJob.getStartDate().tooLong(theJob.getEndDate()) != true) {
			valid = false;
			JOptionPane.showMessageDialog(null, "The job cannot last more than 2 days! Please re-enter the dates.");
		}
		//false if there are more than 5 jobs within 3 days on either side of the end and start date
		for (Job joby : myCurrentJobs){
			Date jobyEnd = joby.getEndDate();
			Date jobyStart = joby.getStartDate();
			jobcount = jobcount + theJob.getStartDate().JobsBefore(jobyEnd) + theJob.getEndDate().JobsBefore(jobyStart);
		}
		if (jobcount > 5) {
			valid = false;
			JOptionPane.showMessageDialog(null, "There are too many jobs already scheduled this week! Please choose another week for your job");
		}
		//false if job is in the past or more than 3 months in the future
		if (theJob.getStartDate().pastOr3Months() != true ) {
			valid = false;
			JOptionPane.showMessageDialog(null, "A job cannot be scheduled in the past or more than 3 months in the future! Please choose another date.");
		}
		return valid;
	}
	
	/**
	 * @return The total number of pending (aka current) jobs in the system. 
	 */
	public int getTotalJobsInQueue() {
		return myCurrentJobs.size();
	}
	
	/**
	 * Adds the current logged in user to the passed in job at the passed in level. 
	 * Checks are done to make sure the volunteer can be added and true is returned if they are. 
	 * 
	 * @param theJob The job the volunteer will be added to. 
	 * @param theLevel 0 for light, 1 for medium, 2 for heavy. 
	 * @return True if the volunteer has been added. 
	 */
	public boolean addToJob(final Job theJob, final int theLevel) {
		boolean added = false;
		for (Job job : myCurrentJobs) {
			if (theJob.equals(job)) {
				if (validateVolunteer(job, theLevel)) {
					job.addVolunteer(myCurrentUser, theLevel);
					
					added = true;
					break;
				}
			}
		}
		return added;
	}
	
	/**
	 * Ensures the volunteer is not signed up for job.
	 * @author Barry
	 * @param theJob
	 * @param theLevel
	 * @return
	 */
	public boolean validateVolunteer(final Job theJob, final int theLevel) {
		boolean valid = theJob.validateVolunteer(myCurrentUser, theLevel);
		for (Job job : getUserJobs()) {
			if (theJob.getStartDate().equals(job.getStartDate())) { 
				valid = false;
				JOptionPane.showMessageDialog(null, "You are already signed-up for a job on this day." +
						" You may not sign up for two jobs on the same day!");
			}
		}
		return valid;
	}
	
	/**
	 * Gets jobs for the logged in user.
	 * @return
	 */
	public Set<Job> getUserJobs() {
		Set<Job> userJobs = new HashSet<Job>();
		if (myCurrentUser.getUsertype() == Usertype.PARKMANAGER) {
			for (Job job : myCurrentJobs) {
				if (job.searchForPM(myCurrentUser)) {
					userJobs.add(job);
				}
			}
		} else if (myCurrentUser.getUsertype() == Usertype.VOLUNTEER) {
			for (Job job : myCurrentJobs) {
				if (job.searchForVols(myCurrentUser)) {
					userJobs.add(job);
				}
			}
		}
		return userJobs;
	}
	
	/**
	 * Blackballs user passed in.
	 * @param theUser
	 * @return blackball status
	 */
	//@requires User is valid and != null
	public boolean blackballUser(User theUser) {
		boolean blackballed = false;
		for (User user : myUsers) {
			if (user.equals(theUser)) {
				//TODO: do we want to check if user is already blackballed?
				user.setBlackballed();
				blackballed = true;
				break;
			}
		}
		return blackballed;
	}
	
	public Set<User> getBlackballed() {
		Set<User> bbUsers = new HashSet<User>();
		for (User user : myUsers) {
			if (user.isBlackballed()) {
				bbUsers.add(user);
			}
		}
		return bbUsers;
	}
	
	
	/**
	 * Searches for the User who is registered under this email, returns true and logs them in if they are 
	 * found. If they are not found false is returned. 
	 * 
	 * @param theEmail The email of the user to login. 
	 * @return True if the user has been logged in. 
	 */
	public boolean setCurrentUser(final String theEmail) {
		for(User currentUser : myUsers) {
			if (currentUser.getEmail().equals(theEmail)) {
				myCurrentUser = currentUser;
				return true;
			}
		}
		return false;
	}

	/**
	 * Gets all jobs for a specific day.
	 * @author Natalie
	 * @param theDate
	 * @return The jobs falling on the day.
	 */
	//@requires theDate is valid and != null
	public Set<Job> getJobOnDay(Date theDate) {
		Set<Job> allJobs = new HashSet<Job>();
		allJobs.addAll(myArchivedJobs);
		allJobs.addAll(myCurrentJobs);
		Set<Job> jobsOnDay = new HashSet<Job>();
		for (Job job: allJobs) {
			if (job.getStartDate().equals(theDate)) {
				jobsOnDay.add(job);
			}
		}
		return jobsOnDay;
	}
	
	public User getCurrentUser() {
		return myCurrentUser;
	}
	
	/**
	 * Saves data to file.
	 */
	//@requires myCurrentJobs != null, myArchivedJobs != null, myUsers != null
	public void saveData() {
		FileIO file = new FileIO();
		file.saveJobs(myCurrentJobs, myArchivedJobs);
		file.saveUsers(myUsers);
	}
	
}
